package dictionary;

import java.math.BigInteger;
import java.util.Random;

public class PallierEncypt {
	private BigInteger p;
	private BigInteger q;
	private BigInteger lambda;
	public BigInteger n;
	public BigInteger nsquare;
	private BigInteger g;
	private int bitLength;

	public PallierEncypt(int bitLengthVal, int certainty) 
	{
	KeyGeneration(bitLengthVal, certainty);
	}
	public PallierEncypt() 
	{
	KeyGeneration(512, 64);
	}
	public void KeyGeneration(int bitLengthVal, int certainty) {
	bitLength = bitLengthVal;
	/*Constructs two randomly generated positive BigIntegers that are probably prime, with the specified bitLength and certainty.*/
	p = new BigInteger(bitLength / 2, certainty, new Random());
	q = new BigInteger(bitLength / 2, certainty, new Random());

	n = p.multiply(q);
	nsquare = n.multiply(n);

	g = new BigInteger("2");
	lambda = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE)).divide(
	p.subtract(BigInteger.ONE).gcd(q.subtract(BigInteger.ONE)));
	/* check whether g is good.*/
	if (g.modPow(lambda, nsquare).subtract(BigInteger.ONE).divide(n).gcd(n).intValue() != 1) {
	System.out.println("g is not good. Choose g again.");
	System.exit(1);
	  }
	 }

	/**
	* Encrypts plaintext m. ciphertext c = g^m * r^n mod n^2. This function automatically generates random input r (to help with encryption).
	* @param m plaintext as a BigInteger
	* @return ciphertext as a BigInteger
	*/
	public BigInteger Encryption(BigInteger m) 
	{
	 BigInteger r = new BigInteger(bitLength, new Random());
	 return g.modPow(m, nsquare).multiply(r.modPow(n, nsquare)).mod(nsquare);
	}

	/**
	* Decrypts ciphertext c. plaintext m = L(c^lambda mod n^2) * u mod n, where u = (L(g^lambda mod n^2))^(-1) mod n.
	* @param c ciphertext as a BigInteger
	* @return plaintext as a BigInteger
	*/
	public BigInteger Decryption(BigInteger c) 
	{
	 BigInteger u = g.modPow(lambda, nsquare).subtract(BigInteger.ONE).divide(n).modInverse(n);
	 return c.modPow(lambda, nsquare).subtract(BigInteger.ONE).divide(n).multiply(u).mod(n);
	 }

}
